Hi,
I'm trying to write an implementation of the RPC spec (level 1, in OCaml). I found a few parts of the spec unclear - could someone clarify them for me?
It says:
[ExportId]
> The exporter chooses an ID before sending a capability over the wire. If
> the capability is already in the table, the exporter should reuse the same ID.
But later:
[CapDescriptor]
> senderHosted @1 :ExportId;
> A capability newly exported by the sender. This is the ID of the new capability in the
> sender's export table (receiver's import table).
How can the exporter reuse the same ID, if it has to be newly exported?
[Message]
> This could be e.g. because the sender received an invalid or nonsensical
> message (`isCallersFault` is true) or because the sender had an internal error
> (`isCallersFault` is false).
isCallersFault appears to be deprecated (`obsoleteIsCallersFault` appears much later).
[Call.sendResultsTo]
> When `yourself` is used, the receiver must still send a `Return` for the call, but sets the
> field `resultsSentElsewhere` in that `Return` rather than including the results.
When should `resultsSentElsewhere` be returned? Once the result is known? Or
once the first takeFromOtherQuestion collects it?
Can takeFromOtherQuestion be used more than once for a single source question?
> The `Call` for bar'() has `sendResultsTo` set to `yourself`, with the value being the
> question ID originally assigned to the bar() call.
What does "the value" refer to here? `yourself` has type `Void`.
> Vat B receives the `Return` for bar'() and sends a `Return` for bar(), with
> `receivedFromYourself` set in place of the results.
`receivedFromYourself` does not appear anywhere else in the spec.
[Return.releaseParamCaps]
> If true, all capabilities that were in the params should be considered released.
Just to be sure: as if the sender had sent a release message for each one with `count=1`?
[Payload]
Why is it not possible to send exceptions in payloads? Should I export each
broken capability as an export and then immediately send a Resolve for each
one, resolving it to an exception?
[Resolve]
> When an export ID sent over the wire (e.g. in a `CapDescriptor`) is indicated to be a promise,
> this indicates that the sender will follow up at some point with a `Resolve` message. If the
> same `promiseId` is sent again before `Resolve`, still only one `Resolve` is sent. If the
> same ID is sent again later _after_ a `Resolve`, it can only be because the export's
> reference count hit zero in the meantime and the ID was re-assigned to a new export, therefore
> this later promise does _not_ correspond to the earlier `Resolve`.
It's not clear to me why it is useful for the receiver to know this.
Presumably the sender can't reuse an export ID until the receiver explicitly releases it anyway.
Should an implementation keep track of whether a resolve has arrived yet and behave differently based on this when it sees an export ID?
> The sender promises that from this point forth, until `promiseId` is released, it shall
> simply forward all messages to the capability designated by `cap`.
Does something similar apply to Return messages? Might be worth mentioning it there too.
[Disembargo]
> Embargos are used to enforce E-order in the presence of promise resolution. That is, if an
> application makes two calls foo() and bar() on the same capability reference, in that order,
> the calls should be delivered in the order in which they were made. But if foo() is called
> on a promise, and that promise happens to resolve before bar() is called, then the two calls
> may travel different paths over the network, and thus could arrive in the wrong order. In
> this case, the call to `bar()` must be embargoed, and a `Disembargo` message must be sent along
> the same path as `foo()` to ensure that the `Disembargo` arrives after `foo()`.
What does "this case" refer to? When exactly is an embargo needed, and when not?
> There are two particular cases where embargos are important. Consider object Alice, in Vat A,
> who holds a promise P, pointing towards Vat B, that eventually resolves to Carol.
Could Carol be another promise here? Should Alice wait until the target is fully resolved before doing a disembargo, or do a disembargo for each step?
[Accept]
> This message is also used to pick up a redirected return -- see `Return.redirect`.
`redirect` doesn't appear anyway else in this spec. I guess it's `Return.sendResultsTo.thirdParty`.
[ Network-specific Parameters]
> For interaction over the global internet between parties with no other prior arrangement, a
> particular set of bindings for these types is defined elsewhere. (TODO(someday): Specify where
> these common definitions live.)
Do these definitions exist now?
--
Thanks!
You received this message because you are subscribed to the Google Groups "Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to capnproto+...@googlegroups.com.
Visit this group at https://groups.google.com/group/capnproto.
To unsubscribe from this group and stop receiving emails from it, send an email to capnproto+unsubscribe@googlegroups.com.
Visit this group at https://groups.google.com/group/capnproto.
--
You received this message because you are subscribed to the Google Groups "Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to capnproto+unsubscribe@googlegroups.com.
Good point. I probably should have cut to the chase and sent my response as a PR. I'll do that as soon as I'm back at a non-phone keyboard.
To unsubscribe from this group and stop receiving emails from it, send an email to capnproto+...@googlegroups.com.
Visit this group at https://groups.google.com/group/capnproto.
--
You received this message because you are subscribed to the Google Groups "Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to capnproto+...@googlegroups.com.
--Daniel Sank
Replies inline (with the disclaimer that I'm not Kenton, my only credentials are that I have stared at this file for a long time):On Wed, Jul 19, 2017 at 1:46 PM Thomas Leonard <tal...@gmail.com> wrote:Hi,
I'm trying to write an implementation of the RPC spec (level 1, in OCaml). I found a few parts of the spec unclear - could someone clarify them for me?
It says:
[ExportId]
> The exporter chooses an ID before sending a capability over the wire. If
> the capability is already in the table, the exporter should reuse the same ID.
But later:
[CapDescriptor]
> senderHosted @1 :ExportId;
> A capability newly exported by the sender. This is the ID of the new capability in the
> sender's export table (receiver's import table).
How can the exporter reuse the same ID, if it has to be newly exported?That seems like a doc/spec typo. You can always specify an existing capability. I think the wording should be something like: "A capability exported by the sender. This may or may not be a new ID in the sender's export table (receiver's import table)."
[Message]
> This could be e.g. because the sender received an invalid or nonsensical
> message (`isCallersFault` is true) or because the sender had an internal error
> (`isCallersFault` is false).
isCallersFault appears to be deprecated (`obsoleteIsCallersFault` appears much later).Yup, Exception has changed (IMO for the better). Instead of placing blame on sender or receiver (such distinctions are hard to draw in general), exceptions are now about what action that caller is advised to take based on the failure.
[Call.sendResultsTo]
> When `yourself` is used, the receiver must still send a `Return` for the call, but sets the
> field `resultsSentElsewhere` in that `Return` rather than including the results.
When should `resultsSentElsewhere` be returned? Once the result is known? Or
once the first takeFromOtherQuestion collects it?(I haven't implemented this for Go yet, but want to.) AFAICT resultsSentElsewhere should be sent once the result is known.
Can takeFromOtherQuestion be used more than once for a single source question?I would assume that it could be used until Finish message is sent for that question, much like other question-based data. In practice, every call's result is held in the answers table until Finish is received.
> The `Call` for bar'() has `sendResultsTo` set to `yourself`, with the value being the
> question ID originally assigned to the bar() call.
What does "the value" refer to here? `yourself` has type `Void`.
> Vat B receives the `Return` for bar'() and sends a `Return` for bar(), with
> `receivedFromYourself` set in place of the results.
`receivedFromYourself` does not appear anywhere else in the spec.I think this whole example is stale and probably needs another draft.
[Return.releaseParamCaps]
> If true, all capabilities that were in the params should be considered released.
Just to be sure: as if the sender had sent a release message for each one with `count=1`?(I might be wrong on this point, it's been a while since I've looked. The docs should probably spell this out.) Usually. The list of CapDescriptors in a Payload could point to the same capability multiple times. A release message of count=1 per CapDescriptor is a more accurate way of phrasing this.
[Payload]
Why is it not possible to send exceptions in payloads? Should I export each
broken capability as an export and then immediately send a Resolve for each
one, resolving it to an exception?Payload is only used for parameters and results. It doesn't make sense for parameters to be an exception, and results is inside a union where you could specify an exception that is an alternative. I'm not sure I understand the use-case where you are sending a broken capability.
[Resolve]
> When an export ID sent over the wire (e.g. in a `CapDescriptor`) is indicated to be a promise,
> this indicates that the sender will follow up at some point with a `Resolve` message. If the
> same `promiseId` is sent again before `Resolve`, still only one `Resolve` is sent. If the
> same ID is sent again later _after_ a `Resolve`, it can only be because the export's
> reference count hit zero in the meantime and the ID was re-assigned to a new export, therefore
> this later promise does _not_ correspond to the earlier `Resolve`.
It's not clear to me why it is useful for the receiver to know this.
Presumably the sender can't reuse an export ID until the receiver explicitly releases it anyway.
Should an implementation keep track of whether a resolve has arrived yet and behave differently based on this when it sees an export ID?It's more specifying that the receiver should not resolve the promise more than once. I believe in this case that it would be a protocol violation, in which case the correct behavior would be for the receiver to send an abort.
> The sender promises that from this point forth, until `promiseId` is released, it shall
> simply forward all messages to the capability designated by `cap`.
Does something similar apply to Return messages? Might be worth mentioning it there too.I believe so, but I don't know/remember. :(
[Disembargo]
> Embargos are used to enforce E-order in the presence of promise resolution. That is, if an
> application makes two calls foo() and bar() on the same capability reference, in that order,
> the calls should be delivered in the order in which they were made. But if foo() is called
> on a promise, and that promise happens to resolve before bar() is called, then the two calls
> may travel different paths over the network, and thus could arrive in the wrong order. In
> this case, the call to `bar()` must be embargoed, and a `Disembargo` message must be sent along
> the same path as `foo()` to ensure that the `Disembargo` arrives after `foo()`.
What does "this case" refer to? When exactly is an embargo needed, and when not?
If you're implementing level 1 (two-party), then really the only place where this applies is when you receive a capability that the receiver hosts as part of a return or resolve after you have made calls on the promised capability. This implies that the RPC system needs to keep track of which parts of the answer have had calls made on them. When this occurs, the receiver gives the application code an embargoed client, and then sends a Disembargo with senderLoopback set. It releases the embargo once the same disembargo ID is returned with receiverLoopback set.For me, this was the hardest part of the spec to understand. I understand why it's needed, but it's really hard to grok the implications.
> There are two particular cases where embargos are important. Consider object Alice, in Vat A,
> who holds a promise P, pointing towards Vat B, that eventually resolves to Carol.
Could Carol be another promise here? Should Alice wait until the target is fully resolved before doing a disembargo, or do a disembargo for each step?See above explanation. But no, Carol cannot be a promise, since the only time that an embargo is triggered is once you get back a locally hosted capability.
[Accept]
> This message is also used to pick up a redirected return -- see `Return.redirect`.
`redirect` doesn't appear anyway else in this spec. I guess it's `Return.sendResultsTo.thirdParty`.Probably. It's Level 3, so it's invisible to me. :D
[ Network-specific Parameters]
> For interaction over the global internet between parties with no other prior arrangement, a
> particular set of bindings for these types is defined elsewhere. (TODO(someday): Specify where
> these common definitions live.)
Do these definitions exist now?¯\_(ツ)_/¯
--
You received this message because you are subscribed to the Google Groups "Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to capnproto+unsubscribe@googlegroups.com.
I thought that must be the reason originally, but it seems that
takeFromOtherQuestion requires sharing even if it can only be used
once, because the struct is held by the original answer (for
pipelining) and also by the question that took it.
Maybe I got this bit wrong. I attached the "used" flags to the>> If you're implementing level 1 (two-party), then really the only place
>> where this applies is when you receive a capability that the receiver hosts
>> as part of a return or resolve after you have made calls on the promised
>> capability. This implies that the RPC system needs to keep track of which
>> parts of the answer have had calls made on them. When this occurs, the
>> receiver gives the application code an embargoed client, and then sends a
>> Disembargo with senderLoopback set. It releases the embargo once the same
>> disembargo ID is returned with receiverLoopback set.
question, but maybe I should be tagging the reference to the question
instead. Can different references to the same question need different
disembargoes? e.g. should forwarding a message mark the promised
answer as needing a disembargo or not?
OK, I'll try to match the C++ behaviour for now.
I did start off trying to implement it that way, but then I realised
that questions don't usually hang around long anyway, so it didn't
seem worth the effort.
>> Maybe I got this bit wrong. I attached the "used" flags to the
>> question, but maybe I should be tagging the reference to the question
>> instead. Can different references to the same question need different
>> disembargoes? e.g. should forwarding a message mark the promised
>> answer as needing a disembargo or not?
>
> Sorry, I don't understand your question here.
Maybe it doesn't make sense, or only with my implementation, but it
seems we have two objects for a question/export:
- a proxy that always sends to the remote peer
- a switchable proxy that forwards to the previous object until the
question returns, and then sends to the new target (possibly after a
disembargo)
I was just wondering which proxy should track whether it has been used
(and, therefore, whether it needs a disembargo).
If we had the implementor's guide that was mentioned earlier, it could
probably cover this. My current implementation muddles these two up,
which is why it's delivering things out of order, so my question is
probably muddled up too. I'll need to think about this a bit more.
Does it alternate between being a disembargo request and a disembargo> Nice example!
>
> It looks like the C++ implementation today will decide b = q1.x, and never
> allow it to further resolve to client_bs. This "works" but is clearly
> suboptimal.
>
> For a correct solution, we need to recognize that Disembargo messages can
> "bounce" multiple times:
response as this happens?
Does the 3-vat case complicate things?